﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Medianamik.Core.Configuration;
using Medianamik.Core.Redirect;
using StructureMap;

namespace Medianamik.Core.Urls
{
    public static class UrlHelper
    {
        public static IEnumerable<IMedianamikUrl> GetUrlsFromXml(String document, CultureInfo culture)
        {
            if (!String.IsNullOrEmpty(document))
                return GetUrlsFromXml(XDocument.Parse(document), culture);

            return Enumerable.Empty<IMedianamikUrl>();
        }

        public static IEnumerable<IMedianamikUrl> GetUrlsFromXml(XDocument document, CultureInfo culture)
        {
            if (document.Root != null)
            {
                foreach (var url in document.Root.Elements(UrlXml.URL_ELEMENT_NAME))
                {
                    if (url.Attribute(UrlXml.WEBSITE_ATT_NAME) != null)
                    {
                        var path = (string)url.Attribute(UrlXml.PATH_ATT_NAME);
                        var webSiteId = (Guid)url.Attribute(UrlXml.WEBSITE_ATT_NAME);
                        var excludedFromIndexation = (bool?)url.Attribute(UrlXml.INDEXATION_ATT_NAME);

                        yield return new MedianamikUrl(webSiteId, culture, path,
                            excludedFromIndexation.GetValueOrDefault());
                    }
                }
            }
        }

        public static XDocument GetXmlFromUrls(this IEnumerable<IMedianamikUrl> urls, Guid nodeId)
        {
            if (urls.Count() == 0)
                return null;

            var value = new XDocument(new XElement(UrlXml.ROOT_ELEMENT_NAME));

            urls.ForEach(url => value.Root.Add(new XElement(UrlXml.URL_ELEMENT_NAME
                         , new XAttribute(UrlXml.PATH_ATT_NAME, url.OriginalPath)
                         , new XAttribute(UrlXml.WEBSITE_ATT_NAME, url.WebsiteId)
                         , new XAttribute(UrlXml.NODE_ATT_NAME, nodeId)
                         , new XAttribute(UrlXml.INDEXATION_ATT_NAME, url.ExcludedFromIndexation))));

            return value;
        }

        public static IEnumerable<IUrlDictionaryEntry> GetDuplicates(this IEnumerable<IUrlDictionaryEntry> entries)
        {
            return entries.GroupBy(e => e.RequestedUrl)
                .Where((url, count) => count > 1)
                .SelectMany((url, count) => url);
        }

        public static string GetPathFromSegments(IPathSegmentFormater segmentFormater, PathSegmentCollection segments)
        {
            var urlBuilder = new StringBuilder();
            segments.ToList().ForEach(s => urlBuilder.Append("/" + segmentFormater.Format(s.Value)));

            return urlBuilder.ToString();
        }

        private static IEnumerable<RedirectNode> _redirectNodes;

        public static UrlCleanUpReport CleanUpUrls(bool publishNodeWhenUrlCleaned)
        {
            _redirectNodes = null;

            var urls = LoadInvalidMedianamikUrls();

            urls.ForEach(url => url.ValidUrl = CleanUpUrl(url.InvalidUrl));

            SwitchInvalidUrl(urls, publishNodeWhenUrlCleaned);
            
            AddSwitchedUrlToRedirectModule(urls);

            if (urls.Any(url => url.SuccessfullyCleaned))
                RedirectDictionary.Instance.Reload();

            return BuildUrlCleanUpReport(urls);
        }

        private static UrlCleanUpReport BuildUrlCleanUpReport(IEnumerable<MedianamikRawUrl> urls)
        {
            var report = new UrlCleanUpReport();

            report.NumberOfInvalidUrls = urls.Count();
            report.NumberOfUrlsCleaned = urls.Count(url => url.SuccessfullyCleaned);
            report.CleanUpErrors =
                urls.Where(url => !url.SuccessfullyCleaned).Select(
                    url =>
                    new CleanUpError {NodeId = url.NodeId, CultureName = url.Culture.Name, ErrorMessage = url.ErrorMessage});

            return report;
        }

        private static void SwitchInvalidUrl(IEnumerable<MedianamikRawUrl> urls, bool publishNodeWhenUrlCleaned)
        {
            foreach (var url in urls)
            {
                try
                {
                    var affectedNode = NodeManager.GetNode(url.NodeId);
                    var urlDoc = XDocument.Parse(affectedNode.Instances[url.Culture].Properties["Url"].GetValue<string>());

                    var urlAttribute = (from urlNode in urlDoc.Root.Elements(UrlXml.URL_ELEMENT_NAME)
                                       from attribute in urlNode.Attributes(UrlXml.PATH_ATT_NAME)
                                       where attribute.Value.EqualsIgnoreCase(url.InvalidUrl)
                                       select attribute).First();

                    urlAttribute.Value = url.ValidUrl;

                    affectedNode.Instances[url.Culture].Properties["Url"].SetValue(urlDoc.ToString());
                    NodeManager.SaveNode(affectedNode);
                    if (publishNodeWhenUrlCleaned)
                        NodeManager.PublishNode(affectedNode.ID, url.Culture, DateTime.Now, null);

                    url.SuccessfullyCleaned = true;
                }
                catch (Exception e)
                {
                    url.ErrorMessage = e.Message;
                }
            }
        }

        private static void AddSwitchedUrlToRedirectModule(IEnumerable<MedianamikRawUrl> urls)
        {
            var medianamikConfigSection = (MedianamikConfigSection)ConfigurationManager.GetSection(ConfigurationSectionName.MEDIANAMIK);

            urls.ForEach(url =>
                             {
                                 var urlWithExtension = url.InvalidUrl + medianamikConfigSection.DefaultPageExtension;
                                 if (url.SuccessfullyCleaned)
                                     if (!NodeExistInRedirectModule(url.NodeId, urlWithExtension))
                                         AddUrlToRedirectModule(url.NodeId, url.Culture, urlWithExtension, 301);
                             });
        }

        private static void AddUrlToRedirectModule(Guid toNodeId, CultureInfo nodeCulture, string urlRedirectFrom, int redirectCode)
        {
            var newNode = NodeManager.NewNode(new RedirectNode().NodeTypeID, nodeCulture,
                                                            MedianamikModules.RedirectManagement);

            
            newNode.DefaultInstance.Name = "invalid - " + urlRedirectFrom;
            newNode.DefaultInstance[MedianamikProperties.MedianamikRedirectTo].SetValue(toNodeId);
            newNode.DefaultInstance[MedianamikProperties.MedianamikRedirectFrom].SetValue(urlRedirectFrom);
            newNode.DefaultInstance[MedianamikProperties.MedianamikRedirectCode].SetValue(redirectCode);
            
            NodeManager.SaveNode(newNode);
        }

        private static bool NodeExistInRedirectModule(Guid toNodeId, string urlRedirectFrom)
        {
            return
                RedirectNodes.Any(node => node.RedirectFrom.EqualsIgnoreCase(urlRedirectFrom) && node.RedirectToNodeId.Equals(toNodeId));
        }

        private static IEnumerable<RedirectNode> RedirectNodes
        {
            get
            {
                if (_redirectNodes == null)
                    _redirectNodes = ConfigManager.GetAll<RedirectNode>();

                return _redirectNodes;
                    
            }
        }

        private static string CleanUpUrl(string url)
        {
            var i = 0;
            var segments = new PathSegmentCollection();

            url.Split('/').ForEach(u => { if (!String.IsNullOrEmpty(u)) segments.Add(i++, new PathSegment(u)); });

            return GetPathFromSegments(new PathSegmentFormater(), segments);
        }

        private static IEnumerable<MedianamikRawUrl> LoadInvalidMedianamikUrls()
        {
            //var urls = DALProviderManager.DALProvider.ConfigDALProvider.GetUrlXmlDocs();
            var urls = ObjectFactory.GetInstance<IUrlRepository>().GetUrlXmlDocs();

            return (from url in urls
                    from rawUrl in GetUrlsFromXml(url.Xml, new CultureInfo("fr-CA"))
                    where !Uri.IsWellFormedUriString(rawUrl.OriginalPath, UriKind.Relative)
                    select (new MedianamikRawUrl {NodeId = url.NodeId, Url = rawUrl, Culture = url.Culture})).ToList();
        }

        public class MedianamikRawUrl
        {
            public Guid NodeId { get; set; }
            public IMedianamikUrl Url { get; set; }
            public string InvalidUrl { get { return Url.OriginalPath;  } }
            public string ValidUrl { get; set; }
            public CultureInfo Culture { get; set; }
            public bool SuccessfullyCleaned { get; set; }
            public string ErrorMessage { get; set; }
        }

        public class UrlCleanUpReport
        {
            public int NumberOfInvalidUrls { get; set; }
            public int NumberOfUrlsCleaned { get; set; }
            public IEnumerable<CleanUpError> CleanUpErrors { get; set; }
        }

        public class CleanUpError
        {
            public Guid NodeId { get; set; }
            public string CultureName { get; set; }
            public string ErrorMessage { get; set; }
        }
    }
}